package com.zmkj.cn.sys.service.impl;

import java.util.HashMap;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.zmkj.cn.comm.baseenum.IndexEnum;
import com.zmkj.cn.comm.constant.RedisConstant;
import com.zmkj.cn.comm.constant.ResultCode;
import com.zmkj.cn.comm.vo.CommonResultVo;
import com.zmkj.cn.comm.vo.RestVo;
import com.zmkj.cn.sys.constant.CommConstant;
import com.zmkj.cn.sys.constant.WechatConstant;
import com.zmkj.cn.sys.service.ISysCommonService;
import com.zmkj.cn.sys.service.ISysLoginService;
import com.zmkj.cn.sys.util.RedisUtil;
import com.zmkj.cn.sys.vo.input.SysHospitalInputVo;
import com.zmkj.cn.sys.vo.input.SysLoginWechatInputVo;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * @author 伍绍宇
 * @date 2021/04/21
 */
@Service
@Slf4j
public class SysLoginServiceImpl implements ISysLoginService {

    @Value("${wx.appId}")
    private String appId;

    @Value("${wx.secret}")
    private String secret;

    @Value("${wx.openid.url}")
    private String openIdUrl;


    @Resource
    private RestTemplate restTemplate;
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private ISysCommonService sysCommonService;

    @Override
    public Object firstLoginWeChat(HttpServletRequest request, SysLoginWechatInputVo loginVo) {
        String str = "";
        if (loginVo.getUnionid() != null && loginVo.getOpenid() != null) {
            str = JSONObject.toJSONString(loginVo);
        } else {
            // 请求微信获取小程序openid与unionid
            str = getWechatOpenId(loginVo);
        }
        log.info("----获取小程序openid与unionid:" + str);
        // 如果获取到了
        if (JSONUtil.isJson(str)) {
            // 解析返回的json
            JSONObject data = JSONObject.parseObject(str);
            int errcode = IndexEnum.ZERO.code;
            if (data.get(WechatConstant.ERRCODE.value) != null) {
                errcode = Integer.parseInt(data.get(WechatConstant.ERRCODE.value).toString());
            }
            if (errcode == IndexEnum.ZERO.code) {
                loginVo.setOpenid(data.get(WechatConstant.OPENID.value).toString());
                loginVo.setUnionid(data.get(WechatConstant.UNIONID.value).toString());
                return loginCore(request, loginVo);
            } else {
                // 错误
                return errorType(errcode);
            }
        } else {
            return CommonResultVo.failed(ResultCode.WX_NET_ERROR);
        }
    }

    /**
     * 注册医院信息
     *
     * @author 伍绍宇
     * @date 2021/04/28
     */
    @Override
    public Object registerHospital(SysHospitalInputVo sysHospitalInputVo) {
        // 创建密钥ACCESS_SECRET
        String access = RandomUtil.randomString(16);
        String secret = RandomUtil.randomString(32);
        // 保存医院详情医院信息
        redisUtil.add(RedisConstant.HOSPITAL_DETAIL.value, access, JSON.toJSONString(sysHospitalInputVo));
        redisUtil.add(RedisConstant.ACCESS_SECRET.value, access, secret);
        redisUtil.add(RedisConstant.HOSPITAL_INFO.value, access, sysHospitalInputVo.getServerUrl());
        log.info("@sunshine:注册医院信息,注册信息(serverName={},serverUrl={};生成的授权信息(access={},secret={}))",sysHospitalInputVo.getServerName(),sysHospitalInputVo.getServerUrl(),access, secret);
        HashMap<String, String> map = Maps.newHashMap();
        map.put("ywt.center.access", access);
        map.put("ywt.center.secret", secret);
        return CommonResultVo.success(map);
    }

    /**
     * 处理错误类型
     *
     * @author 伍绍宇
     * @date 2021/04/28
     */
    private CommonResultVo<Object> errorType(int errcode) {
        if (errcode == ResultCode.WX_CODE_ERROR.getValue()) {
            return CommonResultVo.failed(ResultCode.WX_CODE_ERROR);
        } else if (errcode == ResultCode.WX_BUSY_ERROR.getValue()) {
            return CommonResultVo.failed(ResultCode.WX_BUSY_ERROR);
        } else if (errcode == ResultCode.WX_CODE_COVER.getValue()) {
            return CommonResultVo.failed(ResultCode.WX_CODE_COVER);
        }
        return CommonResultVo.failed(ResultCode.WX_NOT_ERROR);
    }

    /**
     * 登录核心
     *
     * @author 伍绍宇
     * @date 2021/04/28
     */
    private Object loginCore(HttpServletRequest request, SysLoginWechatInputVo loginVo) {
        Object tokenInfo = redisUtil.getMap(RedisConstant.OPEN_TOKENS.value, loginVo.getUnionid());
        if (tokenInfo != null) {
            loginVo.setPhone(tokenInfo.toString());
        }else if (loginVo.getPhone() == null) {
            //如果账号是走无码报修
            if(StringUtils.isNotEmpty(loginVo.getHospitalId())){
                //无码报修
                return loginRepair(loginVo);
            }

            // 如果不存在数据库，返回10
            return CommonResultVo.failed(ResultCode.NOT_RELATION);
        }
        // 判断手机号是否注册机里面
        log.info("----phone =[{}],result [{}]",loginVo.getPhone(),redisUtil.hashKey(RedisConstant.USER_INFO.value, loginVo.getPhone()));
        if (!redisUtil.hashKey(RedisConstant.USER_INFO.value, loginVo.getPhone())) {
            if(StringUtils.isNotEmpty(loginVo.getHospitalId())){
                //无码报修 用户分发器中找不到对应的医院
                return loginRepair(loginVo);
            }
            return CommonResultVo.failed("分发器未注册该号码");
        }
        String userInfo = redisUtil.getMap(RedisConstant.USER_INFO.value, loginVo.getPhone()).toString();
        if(StringUtils.isNotEmpty(loginVo.getHospitalId()) && !loginVo.getHospitalId().equals(userInfo)){
            //无码报修 找到的用户对应的医院但是与传递的医院标识不一致
            return loginRepair(loginVo);
        }
        loginVo.setAccessKey(userInfo);
        loginVo.setSessionKey(redisUtil.getMap(RedisConstant.ACCESS_SECRET.value, userInfo).toString());
        String forwardUrl = redisUtil.getMap(RedisConstant.HOSPITAL_INFO.value, userInfo).toString();
        log.info("转发服务器---------------" + forwardUrl);
        if (StringUtils.isNotEmpty(forwardUrl)) {
            // 获取跳转
            return loginUrl(request, forwardUrl, loginVo);
        }
        return CommonResultVo.failed("系统未录入该手机号或手机号已经被其它微信号绑定");
    }

    /**
     * 无码报修换取token
     * @return
     */
    public Object loginRepair(SysLoginWechatInputVo loginVo){
        Object map = redisUtil.getMap(RedisConstant.HOSPITAL_INFO.value, loginVo.getHospitalId());
        if (map == null) {
            // 找不到对应医院
            return CommonResultVo.failed(ResultCode.HOSPITAL_ERROR);
        }
        //前端要求 返回token 并且需要返回错误码11为报修登录
        CommonResultVo<HashMap<String,Object>> vo = restTemplate.postForObject(map.toString() + "/loginRepair", null, CommonResultVo.class, new HashMap<>());
        vo.setCode(ResultCode.NOT_BIND_REPAIR.getCode());
        //将返回的token植入医院标识
        redisUtil.set(vo.getBody().get("token").toString(),loginVo.getHospitalId());
        log.info("用户的token是:{}",vo.getBody().get("token").toString());
        return vo;
    }

    /**
     * 登录转发
     *
     * @author 伍绍宇
     * @date 2021/04/28
     */
    private RestVo loginUrl(HttpServletRequest request, String forwardUrl, SysLoginWechatInputVo loginVo) {
        RestVo result = sysCommonService.forwardInfo(request, forwardUrl, popHeaders(loginVo), headerMap(loginVo));
        if (StringUtils.isNotEmpty(result.getValue())) {
            JSONObject jsonObject = JSONObject.parseObject(result.getValue());
            // 如果正确返回数据
            if (Integer.parseInt(jsonObject.get(WechatConstant.CODE.value).toString()) >= ResultCode.SUCCESS.getCode()) {
                // 关联
                if (!redisUtil.hashKey(RedisConstant.OPEN_TOKENS.value, loginVo.getUnionid())) {
                    // 手机号与unionid关联
                    redisUtil.add(RedisConstant.OPEN_TOKENS.value, loginVo.getUnionid(), loginVo.getPhone());
                    redisUtil.add(RedisConstant.USER_OPEN.value, loginVo.getPhone(), loginVo.getUnionid());
                    // unionid与小程序openid关联
                    redisUtil.add(RedisConstant.UNION_USER.value, loginVo.getUnionid(), loginVo.getOpenid());
                }
                JSONObject map = JSONObject.parseObject(jsonObject.get(WechatConstant.BODY.value).toString());
                redisUtil.set(map.get(WechatConstant.TOKEN.value).toString(), redisUtil.getMap(RedisConstant.USER_INFO.value, loginVo.getPhone()).toString(), 182 * 24 * 60 * 60);
            }
        }
        return result;
    }

    private Map<String, Object> headerMap(SysLoginWechatInputVo loginVo) {
        Map<String, Object> map = new HashMap<>();
        map.put(CommConstant.ACCESSKEY, loginVo.getAccessKey());
        map.put(CommConstant.SECRETKEY, loginVo.getSessionKey());
        map.put("newUserLogin", loginVo.getNewUserLogin());
        return map;
    }

    /**
     * 组装请求体
     *
     * @author 伍绍宇
     * @date 2021/04/28
     */
    protected MultiValueMap<String, String> popHeaders(SysLoginWechatInputVo loginVo) {
        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
        map.add("phone", loginVo.getPhone());
        map.add("sessionKey", loginVo.getSessionKey());
        map.add("headPortrait", loginVo.getHeadPortrait());
        return map;
    }

    /**
     * 从微信获取openid
     *
     * @param loginVo 参数对象
     * @return openid
     */
    private String getWechatOpenId(SysLoginWechatInputVo loginVo) {
        Map<String, String> paramMap = new HashMap<>(3);
        paramMap.put(WechatConstant.SECRET.value, secret);
        paramMap.put(WechatConstant.APPID.value, appId);
        paramMap.put(WechatConstant.CODE.value, loginVo.getCode());
        return restTemplate.getForObject(openIdUrl, String.class, paramMap);
    }
}